Cumulative Layout Shift (CLS)

浏览器支持

  • Chrome: 77.
  • Edge: 79.
  • Firefox:不支持。
  • Safari:不支持。

来源

意外的布局偏移会以多种方式扰乱用户体验,从文本突然移动导致用户在阅读时失去位置,到使用户点击错误的链接或按钮。在某些情况下,这可能会造成严重损害。

布局的突然偏移使用户确认了他们打算取消的大额订单。

页面内容的意外移动通常发生在异步加载资源或在现有内容之前将 DOM 元素动态添加到页面时。布局偏移的原因可能是尺寸未知的图像或视频、渲染尺寸大于或小于其初始后备字体的字体,或者动态调整自身大小的第三方广告或小部件。

站点在开发中的功能与其用户体验之间的差异使这个问题变得更糟。例如:

  • 个性化内容或第三方内容在开发和生产环境中的行为通常不同。
  • 测试图像通常已在开发人员的浏览器缓存中,但最终用户加载时间较长。
  • 本地运行的 API 调用通常速度非常快,以至于在开发中不易察觉的延迟在生产环境中可能会变得非常明显。

累积布局偏移 (CLS) 指标通过衡量真实用户发生这种情况的频率来帮助您解决此问题。

什么是 CLS?

CLS 衡量的是在页面整个生命周期中发生的每次意外布局偏移的最大布局偏移分数突发。

布局偏移是指任何可见元素在两个渲染帧之间更改其位置时发生的情况。(有关如何计算各个布局偏移分数的详细信息将在本指南的后面部分介绍。)

布局偏移突发(称为会话窗口)是指当一个或多个单独的布局偏移在快速连续发生时,每次偏移之间的时间少于 1 秒,且整个窗口持续时间最长为 5 秒。

最大突发是会话窗口,该窗口具有该窗口内所有布局偏移的最大累积分数。

会话窗口示例。蓝色条表示每个单独布局偏移的分数。

良好的 CLS 分数是多少?

为了提供良好的用户体验,网站应努力使 CLS 分数达到 0.1 或更低。为了确保您为大多数用户实现此目标,一个好的衡量阈值是 第 75 个百分位页面加载量,按移动设备和桌面设备细分。

Good CLS values are 0.1 or less, poor values are greater than 0.25, and anything in between needs improvement
良好的 CLS 值是 0.1 或更低。较差的值大于 0.25。

要了解有关此建议背后的研究和方法的更多信息,请参阅定义核心 Web 指标阈值

布局偏移详情

布局偏移由 布局不稳定性 API 定义,该 API 在任何时候报告 layout-shift 条目,只要视口中可见的元素在两个帧之间更改其起始位置(例如,默认书写模式中的顶部和左侧位置)。此类元素被视为不稳定元素

请注意,布局偏移仅在现有元素更改其起始位置时发生。如果将新元素添加到 DOM 或现有元素更改大小,则不计为布局偏移,只要该更改不会导致其他可见元素更改其起始位置。

布局偏移分数

要计算布局偏移分数,浏览器会查看视口大小以及两个渲染帧之间视口中不稳定元素的移动。布局偏移分数是该移动的两个度量的乘积:影响分数距离分数(都在下面定义)。

layout shift score = impact fraction * distance fraction

影响分数

影响分数衡量不稳定元素在两个帧之间对视口区域的影响程度。

给定帧的影响分数是该帧和前一帧的所有不稳定元素的可见区域的组合,以视口总面积的分数表示。

Impact fraction example with one unstable element
如果元素更改位置,则其先前位置和当前位置都会影响其影响分数。

在上图中,有一个元素在一个帧中占据了视口的一半。然后,在下一帧中,该元素向下移动了视口高度的 25%。红色虚线矩形表示该元素在两个帧中的可见区域的并集,在本例中为视口总面积的 75%,因此其影响分数0.75

距离分数

布局偏移分数方程的另一部分衡量了不稳定元素相对于视口移动的距离。距离分数是任何不稳定元素在帧中移动的最大水平或垂直距离,除以视口的最大尺寸(宽度或高度,以较大者为准)。

Distance fraction example with one unstable element
距离分数衡量元素在视口中移动的距离。

在前面的示例中,最大的视口尺寸是高度,而不稳定元素已移动了视口高度的 25%,这使得距离分数为 0.25。

因此,在本例中,影响分数0.75距离分数0.25,因此布局偏移分数0.75 * 0.25 = 0.1875

示例

下一个示例说明了向现有元素添加内容如何影响布局偏移分数:

Layout shift example with multiple stable and _unstable elements_
在灰色框底部添加一个按钮会将绿色框向下推,并部分移出视口。

在此示例中,灰色框更改了大小,但其起始位置未更改,因此它不是不稳定元素

“点我!”按钮以前不在 DOM 中,因此其起始位置也不会更改。

但是,绿色框的起始位置确实发生了更改,但由于它已部分移出视口,因此在计算影响分数时,不考虑不可见区域。绿色框在两个帧中的可见区域的并集(如红色虚线矩形所示)与第一帧中绿色框的面积相同 - 视口的 50%。影响分数0.5

距离分数用紫色箭头表示。绿色框已向下移动了约视口的 14%,因此距离分数0.14

布局偏移分数为 0.5 x 0.14 = 0.07

以下示例显示了多个不稳定元素如何影响页面的布局偏移分数:

Layout shift example with stable and _unstable elements_ and viewport clipping
随着排序列表中显示更多名称,现有名称会移动以保持字母顺序。

在上图的第一个帧中,有四个动物 API 请求结果,按字母顺序排序。在第二个帧中,更多结果被添加到排序列表中。

列表中的第一个项目(“Cat”)在帧之间不会更改其起始位置,因此它是稳定的。同样,添加到列表中的新项目以前不在 DOM 中,因此它们的起始位置也不会更改。但是,标记为“Dog”、“Horse”和“Zebra”的项目都会偏移其起始位置,从而使它们成为不稳定元素

同样,红色虚线矩形表示这三个不稳定元素的前后区域的并集,在本例中约为视口面积的 60%(影响分数0.60)。

箭头表示不稳定元素从其起始位置移动的距离。蓝色箭头表示的“Zebra”元素移动得最远,约为视口高度的 30%。这使得本例中的距离分数0.3

布局偏移分数为 0.60 x 0.3 = 0.18

预期与意外的布局偏移

并非所有布局偏移都是有害的。事实上,许多动态 Web 应用程序经常更改页面上元素的起始位置。只有当用户没有预期到布局偏移时,布局偏移才是糟糕的。

用户发起的布局偏移

响应用户交互(例如,单击或点击链接、按下按钮或在搜索框中键入内容)而发生的布局偏移通常是可以接受的,只要偏移发生的时间足够接近交互,以至于用户可以清楚地了解其关联性。

例如,如果用户交互触发的网络请求可能需要一段时间才能完成,则最好立即创建一些空间并显示加载指示器,以避免在请求完成时出现令人不快的布局偏移。如果用户没有意识到正在加载内容,或者不清楚资源何时准备就绪,他们可能会尝试在等待时单击其他内容,而这些内容可能会从他们点击的位置移开。

在用户输入后 500 毫秒内发生的布局偏移将设置 hadRecentInput 标志,因此可以将它们从计算中排除。

动画和过渡

如果动画和过渡效果良好,它们是更新页面内容而不会让用户感到惊讶的好方法。页面上突然和意外移动的内容几乎总是会产生糟糕的用户体验。但是,从一个位置逐渐且自然地移动到下一个位置的内容通常可以帮助用户更好地了解正在发生的事情,并在状态更改之间引导他们。

请务必遵守 prefers-reduced-motion 浏览器设置,因为某些网站访问者可能会因动画而产生不良影响或注意力问题。

CSS transform 属性允许您在不触发布局偏移的情况下为元素设置动画:

  • 不要更改 heightwidth 属性,而应使用 transform: scale()
  • 要移动元素,请避免更改 toprightbottomleft 属性,而应使用 transform: translate()

如何衡量 CLS

CLS 可以在实验室现场进行衡量,并且在以下工具中可用:

现场工具

实验室工具

在 JavaScript 中衡量布局偏移

要在 JavaScript 中衡量布局偏移,您可以使用布局不稳定性 API

以下示例显示了如何创建 PerformanceObserver 以将 layout-shift 条目记录到控制台:

new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    console.log('Layout shift:', entry);
  }
}).observe({type: 'layout-shift', buffered: true});

在 JavaScript 中衡量 CLS

要在 JavaScript 中衡量 CLS,您需要将这些意外的 layout-shift 条目分组到会话中,并计算最大会话值。您可以参考 web vitals JavaScript 库源代码,其中包含有关如何计算 CLS 的参考实现。

在大多数情况下,页面卸载时的当前 CLS 值是该页面的最终 CLS 值,但下一节中指出的几个重要例外情况除外。web vitals JavaScript 库在 Web API 的限制范围内尽可能多地考虑了这些情况。

指标与 API 之间的差异

  • 如果页面在后台加载,或者如果在浏览器绘制任何内容之前将其置于后台,则不应报告任何 CLS 值。
  • 如果页面从后退/前进缓存恢复,则其 CLS 值应重置为零,因为用户将其体验为不同的页面访问。
  • API 不会报告 iframe 中发生的偏移的 layout-shift 条目,但指标会报告,因为它们是页面用户体验的一部分。这可能会显示为 CrUX 和 RUM 之间的差异。为了正确衡量 CLS,您应该考虑它们。子帧可以使用 API 将其 layout-shift 条目报告给父帧以进行聚合

除了这些例外情况之外,CLS 还具有一些额外的复杂性,因为它衡量的是页面的整个生命周期:

  • 用户可能会将选项卡打开非常长的时间 - 几天、几周、几个月。事实上,用户可能永远不会关闭选项卡。
  • 在移动操作系统上,浏览器通常不会为后台选项卡运行页面卸载回调,这使得难以报告“最终”值。

为了处理这种情况,CLS 应该在页面置于后台的任何时候报告,以及在页面卸载的任何时候报告(visibilitychange 事件涵盖了这两种情况)。然后,接收此数据的分析系统将需要在后端计算最终的 CLS 值。

与其自己记忆和处理所有这些情况,开发者可以使用 web-vitals JavaScript 库 来测量 CLS,该库考虑了之前提到的所有情况,除了 iframe 的情况。

import {onCLS} from 'web-vitals';

// Measure and log CLS in all situations
// where it needs to be reported.
onCLS(console.log);

如何改进 CLS

有关在实际应用中识别布局偏移以及使用实验室数据优化它们的更多指导,请参阅我们的 优化 CLS 指南。

其他资源

更新日志

有时,在用于测量指标的 API 中会发现错误,有时在指标本身的定义中也会发现错误。因此,有时必须进行更改,这些更改可能会在您的内部报告和仪表板中显示为改进或退步。

为了帮助您管理这一点,对这些指标的实现或定义的任何更改都将在此更新日志中显示。

如果您对这些指标有反馈,可以在 web-vitals-feedback Google 论坛中提供。